/*
 * Copyright (c) 2005-2009 Substance Kirill Grouchnikov. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *  o Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer. 
 *     
 *  o Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution. 
 *     
 *  o Neither the name of Substance Kirill Grouchnikov nor the names of 
 *    its contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission. 
 *     
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */
package org.jvnet.substance.api;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.*;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.BasicBorders;
import javax.swing.plaf.basic.BasicBorders.MarginBorder;
import javax.swing.table.JTableHeader;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.JTextComponent;

import org.jvnet.lafwidget.animation.FadeKind;
import org.jvnet.substance.SubstanceLookAndFeel;
import org.jvnet.substance.api.ComponentState.ColorSchemeKind;
import org.jvnet.substance.api.renderers.SubstanceDefaultListCellRenderer;
import org.jvnet.substance.api.trait.SubstanceTrait;
import org.jvnet.substance.painter.border.SubstanceBorderPainter;
import org.jvnet.substance.painter.decoration.*;
import org.jvnet.substance.painter.gradient.SubstanceGradientPainter;
import org.jvnet.substance.painter.highlight.SubstanceHighlightPainter;
import org.jvnet.substance.shaper.SubstanceButtonShaper;
import org.jvnet.substance.utils.*;
import org.jvnet.substance.utils.border.*;
import org.jvnet.substance.utils.icon.*;
import org.jvnet.substance.utils.scroll.SubstanceScrollPaneBorder;
import org.jvnet.substance.watermark.SubstanceWatermark;

/**
 * Base abstract class for Substance skins.
 * 
 * @author Kirill Grouchnikov
 */
@SubstanceApi
public abstract class SubstanceSkin implements SubstanceTrait, Cloneable {
	/**
	 * Maps decoration area type to the color scheme bundles. Must contain an
	 * entry for {@link DecorationAreaType#NONE}.
	 */
	protected Map<DecorationAreaType, SubstanceColorSchemeBundle> colorSchemeBundleMap;

	/**
	 * Maps decoration area type to the background color schemes.
	 */
	protected Map<DecorationAreaType, SubstanceColorScheme> backgroundColorSchemeMap;

	/**
	 * The watermark of <code>this</code> skin. May be <code>null</code> if
	 * <code>this</code> skin doesn't define a custom watermark.
	 */
	protected SubstanceWatermark watermark;

	/**
	 * The button shaper of <code>this</code> skin. Must be non-
	 * <code>null</code>.
	 */
	protected SubstanceButtonShaper buttonShaper;

	/**
	 * The gradient painter of <code>this</code> skin. Must be non-
	 * <code>null</code>.
	 */
	protected SubstanceGradientPainter gradientPainter;

	/**
	 * The border painter of <code>this</code> skin. Must be non-
	 * <code>null</code>.
	 */
	protected SubstanceBorderPainter borderPainter;

	/**
	 * The highlight painter of <code>this</code> skin. Must be non-
	 * <code>null</code>.
	 */
	protected SubstanceHighlightPainter highlightPainter;

	/**
	 * The decoration painter of <code>this</code> skin. Must be non-
	 * <code>null</code>.
	 */
	protected SubstanceDecorationPainter decorationPainter;

	/**
	 * Set of all decoration area types that are not explicitly registered in
	 * {@link #colorSchemeBundleMap} but still are considered as decoration
	 * areas in this skin. Controls lying in such areas will have their
	 * background painted by
	 * {@link SubstanceDecorationPainter#paintDecorationArea(Graphics2D, Component, DecorationAreaType, int, int, SubstanceSkin)}
	 * instead of a simple background fill.
	 */
	protected Set<DecorationAreaType> decoratedAreaSet;

	/**
	 * The start of fade effect on the selected tabs in {@link JTabbedPane}s.
	 * 
	 * @see #selectedTabFadeEnd
	 */
	protected double selectedTabFadeStart;

	/**
	 * The end of fade effect on the selected tabs in {@link JTabbedPane}s.
	 * 
	 * @see #selectedTabFadeStart
	 */
	protected double selectedTabFadeEnd;

	/**
	 * Contains the types of decoration areas that show a drop shadow on the few
	 * top pixels.
	 */
	protected Set<DecorationAreaType> dropShadowsSet;

	/**
	 * Color scheme for watermarks.
	 */
	protected SubstanceColorScheme watermarkScheme;

	/**
	 * All component states that have associated non-trivial alpha values.
	 */
	Set<ComponentState> statesWithAlpha;

	/**
	 * The default color scheme of this skin.
	 */
	SubstanceColorScheme defaultColorScheme;

	/**
	 * Constructs the basic data structures for a skin.
	 */
	protected SubstanceSkin() {
		this.colorSchemeBundleMap = new EnumMap<DecorationAreaType, SubstanceColorSchemeBundle>(
				DecorationAreaType.class);
		this.backgroundColorSchemeMap = new EnumMap<DecorationAreaType, SubstanceColorScheme>(
				DecorationAreaType.class);

		this.decoratedAreaSet = EnumSet.of(
				DecorationAreaType.PRIMARY_TITLE_PANE,
				DecorationAreaType.SECONDARY_TITLE_PANE);

		this.dropShadowsSet = EnumSet.noneOf(DecorationAreaType.class);

		this.selectedTabFadeStart = 0.1;
		this.selectedTabFadeEnd = 0.3;

		this.statesWithAlpha = EnumSet.noneOf(ComponentState.class);
	}

	/**
	 * Returns the main active color scheme for this skin. The result is the
	 * active color scheme for controls that do not lie in any decoration area.
	 * Custom painting code that needs to consult the colors of the specific
	 * component should use {@link #getColorScheme(Component, ComponentState)}
	 * method and various {@link SubstanceColorScheme} methods.
	 * 
	 * @return The main active color scheme for this skin.
	 * @see #getColorScheme(Component, ComponentState)
	 * @see #getMainActiveColorScheme(DecorationAreaType)
	 */
	public SubstanceColorScheme getMainActiveColorScheme() {
		return this.colorSchemeBundleMap.get(DecorationAreaType.NONE)
				.getActiveColorScheme();
	}

	/**
	 * Returns the main default color scheme for this skin. The result is the
	 * default color scheme for controls that do not lie in any decoration area.
	 * Custom painting code that needs to consult the colors of the specific
	 * component should use {@link #getColorScheme(Component, ComponentState)}
	 * method and various {@link SubstanceColorScheme} methods.
	 * 
	 * @return The main default color scheme for this skin.
	 * @see #getColorScheme(Component, ComponentState)
	 * @see #getMainDefaultColorScheme(DecorationAreaType)
	 */
	public SubstanceColorScheme getMainDefaultColorScheme() {
		return this.colorSchemeBundleMap.get(DecorationAreaType.NONE)
				.getDefaultColorScheme();
	}

	/**
	 * Returns the main disabled color scheme for this skin. The result is the
	 * disabled color scheme for controls that do not lie in any decoration
	 * area. Custom painting code that needs to consult the colors of the
	 * specific component should use
	 * {@link #getColorScheme(Component, ComponentState)} method and various
	 * {@link SubstanceColorScheme} methods.
	 * 
	 * @return The main disabled color scheme for this skin.
	 * @see #getColorScheme(Component, ComponentState)
	 * @see #getMainDisabledColorScheme(DecorationAreaType)
	 */
	public SubstanceColorScheme getMainDisabledColorScheme() {
		return this.colorSchemeBundleMap.get(DecorationAreaType.NONE)
				.getDisabledColorScheme();
	}

	/**
	 * Returns the watermark of this skin.
	 * 
	 * @return The watermark of this skin. May be <code>null</code>.
	 */
	public SubstanceWatermark getWatermark() {
		return this.watermark;
	}

	/**
	 * Returns the border painter of this skin.
	 * 
	 * @return The border painter of this skin. A valid skin cannot have a
	 *         <code>null</code> value returned from this method. Call
	 *         {@link #isValid()
	 * 	} to verify that the skin is valid.
	 * @see #isValid()
	 */
	public SubstanceBorderPainter getBorderPainter() {
		return this.borderPainter;
	}

	/**
	 * Returns the button shaper of this skin.
	 * 
	 * @return The button shaper of this skin. A valid skin cannot have a
	 *         <code>null</code> value returned from this method. Call
	 *         {@link #isValid()
	 * 	} to verify that the skin is valid.
	 * @see #isValid()
	 */
	public SubstanceButtonShaper getButtonShaper() {
		return this.buttonShaper;
	}

	/**
	 * Returns the gradient painter of this skin.
	 * 
	 * @return The gradient painter of this skin. A valid skin cannot have a
	 *         <code>null</code> value returned from this method. Call
	 *         {@link #isValid()
	 * 	} to verify that the skin is valid.
	 * @see #isValid()
	 */
	public SubstanceGradientPainter getGradientPainter() {
		return this.gradientPainter;
	}

	/**
	 * Returns the highlight painter of this skin.
	 * 
	 * @return The highlight painter of this skin. A valid skin cannot have a
	 *         <code>null</code> value returned from this method. Call
	 *         {@link #isValid()
	 * 	} to verify that the skin is valid.
	 * @see #isValid()
	 */
	public SubstanceHighlightPainter getHighlightPainter() {
		return this.highlightPainter;
	}

	/**
	 * Returns the decoration painter of this skin.
	 * 
	 * @return The decoration painter of this skin. A valid skin cannot have a
	 *         <code>null</code> value returned from this method. Call
	 *         {@link #isValid()
	 * 	} to verify that the skin is valid.
	 * @see #isValid()
	 */
	public SubstanceDecorationPainter getDecorationPainter() {
		return this.decorationPainter;
	}

	/**
	 * Adds skin-specific entries to the UI defaults table.
	 * 
	 * @param table
	 *            UI defaults table.
	 */
	public void addCustomEntriesToTable(UIDefaults table) {
		// Apparently this function is called with null table
		// when the application is run with -Dswing.defaultlaf
		// setting. In this case, this function will be called
		// second time with correct table.
		if (table == null) {
			return;
		}

		Object menuArrowIcon = new UIDefaults.LazyValue() {
			public Object createValue(UIDefaults table) {
				return new MenuArrowIcon(null);
			}
		};

		Object listCellRendererActiveValue = new UIDefaults.ActiveValue() {
			public Object createValue(UIDefaults table) {
				return new SubstanceDefaultListCellRenderer.SubstanceUIResource();
			}
		};

		SubstanceColorScheme mainActiveScheme = this.getMainActiveColorScheme();
		SubstanceColorScheme mainDefaultScheme = this
				.getMainDefaultColorScheme();
		SubstanceColorScheme mainDisabledScheme = this
				.getMainDisabledColorScheme();
		Color controlText = new ColorUIResource(mainActiveScheme
				.getLightColor());
		Color foregroundColor = SubstanceColorUtilities
				.getForegroundColor(mainDefaultScheme);
		Color backgroundActiveColor = new ColorUIResource(mainActiveScheme
				.getBackgroundFillColor());
		Color backgroundDefaultColor = new ColorUIResource(mainDefaultScheme
				.getBackgroundFillColor());
		Color backgroundLightColor = new ColorUIResource(mainActiveScheme
				.getLightBackgroundFillColor());

		Color disabledForegroundColor = SubstanceColorUtilities
				.getForegroundColor(mainDisabledScheme);
		Color disabledTextComponentForegroundColor = disabledForegroundColor;
		float alpha = this.getAlpha(null, ComponentState.DISABLED_UNSELECTED);
		if (alpha < 1.0f) {
			ColorUIResource defaultTextBackgroundColor = SubstanceColorUtilities
					.getDefaultBackgroundColor(JTextComponent.class, this,
							false);
			disabledTextComponentForegroundColor = new ColorUIResource(
					SubstanceColorUtilities.getInterpolatedColor(
							disabledTextComponentForegroundColor,
							defaultTextBackgroundColor, alpha));
		}

		Color lineColor = new ColorUIResource(mainActiveScheme.getLineColor());

		Color lineColorDefault = new ColorUIResource(mainDefaultScheme
				.getLineColor());

		int lcb = SubstanceColorUtilities
				.getColorBrightness(lineColor.getRGB());
		Color lineBwColor = new ColorUIResource(new Color(lcb, lcb, lcb));

		SubstanceColorScheme highlightColorScheme = this.getColorScheme(null,
				ColorSchemeAssociationKind.HIGHLIGHT, ComponentState.SELECTED);
		if (highlightColorScheme == null) {
			highlightColorScheme = this.getColorScheme(null,
					ComponentState.ROLLOVER_SELECTED);
		}

		Color selectionBackgroundColor = new ColorUIResource(
				highlightColorScheme.getSelectionBackgroundColor());

		Color selectionForegroundColor = new ColorUIResource(
				highlightColorScheme.getSelectionForegroundColor());

		Color selectionCellForegroundColor = new ColorUIResource(
				highlightColorScheme.getForegroundColor());

		Color selectionCellBackgroundColor = new ColorUIResource(
				highlightColorScheme.getBackgroundFillColor());

		Object regularMarginBorder = new UIDefaults.LazyValue() {
			public Object createValue(UIDefaults table) {
				return new BorderUIResource.CompoundBorderUIResource(
						new SubstanceBorder(), new BasicBorders.MarginBorder());
			}
		};

		Object textBorder = new UIDefaults.LazyValue() {
			public Object createValue(UIDefaults table) {
				return new BorderUIResource.CompoundBorderUIResource(
						new SubstanceBorder(SubstanceSizeUtils
								.getTextBorderInsets(SubstanceSizeUtils
										.getControlFontSize())),
						new BasicBorders.MarginBorder());
			}
		};

		Object tooltipBorder = new UIDefaults.LazyValue() {
			public Object createValue(UIDefaults table) {
				return new SubstanceBorder(SubstanceSizeUtils
						.getToolTipBorderInsets(SubstanceSizeUtils
								.getControlFontSize()));
			}
		};

		Object comboBorder = new UIDefaults.LazyValue() {
			public Object createValue(UIDefaults table) {
				return new SubstanceBorder(SubstanceSizeUtils
						.getComboBorderInsets(SubstanceSizeUtils
								.getControlFontSize()));
			}
		};

		Object spinnerBorder = new UIDefaults.LazyValue() {
			public Object createValue(UIDefaults table) {
				return new BorderUIResource.CompoundBorderUIResource(
						new SubstanceBorder(SubstanceSizeUtils
								.getSpinnerBorderInsets(SubstanceSizeUtils
										.getControlFontSize())),
						new BasicBorders.MarginBorder());
			}
		};

		SubstanceColorSchemeBundle titlePaneBundle = this.colorSchemeBundleMap
				.containsKey(DecorationAreaType.PRIMARY_TITLE_PANE) ? this.colorSchemeBundleMap
				.get(DecorationAreaType.PRIMARY_TITLE_PANE)
				: this.colorSchemeBundleMap.get(DecorationAreaType.NONE);
		final SubstanceColorScheme titlePaneScheme = titlePaneBundle
				.getActiveColorScheme();

		Object menuItemInsets = new UIDefaults.LazyValue() {
			public Object createValue(UIDefaults table) {
				int menuItemMargin = SubstanceSizeUtils
						.getMenuItemMargin(SubstanceSizeUtils
								.getComponentFontSize(null));
				return new InsetsUIResource(menuItemMargin, menuItemMargin,
						menuItemMargin, menuItemMargin);
			}
		};

		Object fieldInputMap = new UIDefaults.LazyInputMap(new Object[] {
				"ctrl C", DefaultEditorKit.copyAction,

				"ctrl V", DefaultEditorKit.pasteAction,

				"ctrl X", DefaultEditorKit.cutAction,

				"COPY", DefaultEditorKit.copyAction,

				"PASTE", DefaultEditorKit.pasteAction,

				"CUT", DefaultEditorKit.cutAction,

				"shift LEFT", DefaultEditorKit.selectionBackwardAction,

				"shift KP_LEFT", DefaultEditorKit.selectionBackwardAction,

				"shift RIGHT", DefaultEditorKit.selectionForwardAction,

				"shift KP_RIGHT", DefaultEditorKit.selectionForwardAction,

				"ctrl LEFT", DefaultEditorKit.previousWordAction,

				"ctrl KP_LEFT", DefaultEditorKit.previousWordAction,

				"ctrl RIGHT", DefaultEditorKit.nextWordAction,

				"ctrl KP_RIGHT", DefaultEditorKit.nextWordAction,

				"ctrl shift LEFT",
				DefaultEditorKit.selectionPreviousWordAction,

				"ctrl shift KP_LEFT",
				DefaultEditorKit.selectionPreviousWordAction,

				"ctrl shift RIGHT", DefaultEditorKit.selectionNextWordAction,

				"ctrl shift KP_RIGHT",
				DefaultEditorKit.selectionNextWordAction,

				"ctrl A", DefaultEditorKit.selectAllAction,

				"HOME", DefaultEditorKit.beginLineAction,

				"END", DefaultEditorKit.endLineAction,

				"shift HOME", DefaultEditorKit.selectionBeginLineAction,

				"shift END", DefaultEditorKit.selectionEndLineAction,

				"BACK_SPACE", DefaultEditorKit.deletePrevCharAction,

				"ctrl H", DefaultEditorKit.deletePrevCharAction,

				"DELETE", DefaultEditorKit.deleteNextCharAction,

				"RIGHT", DefaultEditorKit.forwardAction,

				"LEFT", DefaultEditorKit.backwardAction,

				"KP_RIGHT", DefaultEditorKit.forwardAction,

				"KP_LEFT", DefaultEditorKit.backwardAction,

				"ENTER", JTextField.notifyAction,

				"ctrl BACK_SLASH",
				"unselect"/* DefaultEditorKit.unselectAction */,

				"control shift O", "toggle-componentOrientation"/*
																 * DefaultEditorKit.
																 * toggleComponentOrientation
																 */
		});

		Object passwordInputMap = new UIDefaults.LazyInputMap(new Object[] {
				"ctrl C", DefaultEditorKit.copyAction,

				"ctrl V", DefaultEditorKit.pasteAction,

				"ctrl X", DefaultEditorKit.cutAction,

				"COPY", DefaultEditorKit.copyAction,

				"PASTE", DefaultEditorKit.pasteAction,

				"CUT", DefaultEditorKit.cutAction,

				"shift LEFT", DefaultEditorKit.selectionBackwardAction,

				"shift KP_LEFT", DefaultEditorKit.selectionBackwardAction,

				"shift RIGHT", DefaultEditorKit.selectionForwardAction,

				"shift KP_RIGHT", DefaultEditorKit.selectionForwardAction,

				"ctrl LEFT", DefaultEditorKit.beginLineAction,

				"ctrl KP_LEFT", DefaultEditorKit.beginLineAction,

				"ctrl RIGHT", DefaultEditorKit.endLineAction,

				"ctrl KP_RIGHT", DefaultEditorKit.endLineAction,

				"ctrl shift LEFT", DefaultEditorKit.selectionBeginLineAction,

				"ctrl shift KP_LEFT",
				DefaultEditorKit.selectionBeginLineAction,

				"ctrl shift RIGHT", DefaultEditorKit.selectionEndLineAction,

				"ctrl shift KP_RIGHT", DefaultEditorKit.selectionEndLineAction,

				"ctrl A", DefaultEditorKit.selectAllAction,

				"HOME", DefaultEditorKit.beginLineAction,

				"END", DefaultEditorKit.endLineAction,

				"shift HOME", DefaultEditorKit.selectionBeginLineAction,

				"shift END", DefaultEditorKit.selectionEndLineAction,

				"BACK_SPACE", DefaultEditorKit.deletePrevCharAction,

				"ctrl H", DefaultEditorKit.deletePrevCharAction,

				"DELETE", DefaultEditorKit.deleteNextCharAction,

				"RIGHT", DefaultEditorKit.forwardAction,

				"LEFT", DefaultEditorKit.backwardAction,

				"KP_RIGHT", DefaultEditorKit.forwardAction,

				"KP_LEFT", DefaultEditorKit.backwardAction,

				"ENTER", JTextField.notifyAction,

				"ctrl BACK_SLASH", "unselect",

				"control shift O", "toggle-componentOrientation" });

		Object multilineInputMap = new UIDefaults.LazyInputMap(new Object[] {
				"ctrl C", DefaultEditorKit.copyAction,

				"ctrl V", DefaultEditorKit.pasteAction,

				"ctrl X", DefaultEditorKit.cutAction,

				"COPY", DefaultEditorKit.copyAction,

				"PASTE", DefaultEditorKit.pasteAction,

				"CUT", DefaultEditorKit.cutAction,

				"shift LEFT", DefaultEditorKit.selectionBackwardAction,

				"shift KP_LEFT", DefaultEditorKit.selectionBackwardAction,

				"shift RIGHT", DefaultEditorKit.selectionForwardAction,

				"shift KP_RIGHT", DefaultEditorKit.selectionForwardAction,

				"ctrl LEFT", DefaultEditorKit.previousWordAction,

				"ctrl KP_LEFT", DefaultEditorKit.previousWordAction,

				"ctrl RIGHT", DefaultEditorKit.nextWordAction,

				"ctrl KP_RIGHT", DefaultEditorKit.nextWordAction,

				"ctrl shift LEFT",
				DefaultEditorKit.selectionPreviousWordAction,

				"ctrl shift KP_LEFT",
				DefaultEditorKit.selectionPreviousWordAction,

				"ctrl shift RIGHT", DefaultEditorKit.selectionNextWordAction,

				"ctrl shift KP_RIGHT",
				DefaultEditorKit.selectionNextWordAction,

				"ctrl A", DefaultEditorKit.selectAllAction,

				"HOME", DefaultEditorKit.beginLineAction,

				"END", DefaultEditorKit.endLineAction,

				"shift HOME", DefaultEditorKit.selectionBeginLineAction,

				"shift END", DefaultEditorKit.selectionEndLineAction,

				"UP", DefaultEditorKit.upAction,

				"KP_UP", DefaultEditorKit.upAction,

				"DOWN", DefaultEditorKit.downAction,

				"KP_DOWN", DefaultEditorKit.downAction,

				"PAGE_UP", DefaultEditorKit.pageUpAction,

				"PAGE_DOWN", DefaultEditorKit.pageDownAction,

				"shift PAGE_UP", "selection-page-up",

				"shift PAGE_DOWN", "selection-page-down",

				"ctrl shift PAGE_UP", "selection-page-left",

				"ctrl shift PAGE_DOWN", "selection-page-right",

				"shift UP", DefaultEditorKit.selectionUpAction,

				"shift KP_UP", DefaultEditorKit.selectionUpAction,

				"shift DOWN", DefaultEditorKit.selectionDownAction,

				"shift KP_DOWN", DefaultEditorKit.selectionDownAction,

				"ENTER", DefaultEditorKit.insertBreakAction,

				"BACK_SPACE", DefaultEditorKit.deletePrevCharAction,

				"ctrl H", DefaultEditorKit.deletePrevCharAction,

				"DELETE", DefaultEditorKit.deleteNextCharAction,

				"RIGHT", DefaultEditorKit.forwardAction,

				"LEFT", DefaultEditorKit.backwardAction,

				"KP_RIGHT", DefaultEditorKit.forwardAction,

				"KP_LEFT", DefaultEditorKit.backwardAction,

				"TAB", DefaultEditorKit.insertTabAction,

				"ctrl BACK_SLASH", "unselect",

				"ctrl HOME", DefaultEditorKit.beginAction,

				"ctrl END", DefaultEditorKit.endAction,

				"ctrl shift HOME", DefaultEditorKit.selectionBeginAction,

				"ctrl shift END", DefaultEditorKit.selectionEndAction,

				"ctrl T", "next-link-action",

				"ctrl shift T", "previous-link-action",

				"ctrl SPACE", "activate-link-action",

				"control shift O", "toggle-componentOrientation" });

		Object emptyIcon = new UIDefaults.LazyValue() {
			public Object createValue(UIDefaults table) {
				return new IconUIResource(new Icon() {
					public int getIconHeight() {
						return 2;
					}

					public int getIconWidth() {
						return 2;
					}

					public void paintIcon(Component c, Graphics g, int x, int y) {
					}
				});
			}
		};

		Object[] defaults = new Object[] {
				"control",
				controlText,

				"TextField.focusInputMap",
				fieldInputMap,

				"PasswordField.focusInputMap",
				passwordInputMap,

				"TextArea.focusInputMap",
				multilineInputMap,

				"TextPane.focusInputMap",
				multilineInputMap,

				"EditorPane.focusInputMap",
				multilineInputMap,

				"Button.defaultButtonFollowsFocus",
				Boolean.FALSE,

				"Button.disabledText",
				disabledForegroundColor,

				"Button.foreground",
				foregroundColor,

				"Button.margin",
				new InsetsUIResource(0, 0, 0, 0),

				"CheckBox.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JCheckBox.class, this, false),

				"CheckBox.border",
				new BorderUIResource.CompoundBorderUIResource(
						SubstanceSizeUtils.getCheckBoxBorder(SubstanceSizeUtils
								.getControlFontSize()), new MarginBorder()),

				"CheckBox.disabledText",
				disabledForegroundColor,

				"CheckBox.foreground",
				foregroundColor,

				"CheckBoxMenuItem.acceleratorForeground",
				foregroundColor,

				"CheckBoxMenuItem.acceleratorSelectionForeground",
				foregroundColor,

				"CheckBoxMenuItem.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JCheckBoxMenuItem.class, this, false),

				"CheckBoxMenuItem.borderPainted",
				Boolean.FALSE,

				"CheckBoxMenuItem.checkIcon",
				new CheckBoxMenuItemIcon(null, 1 + SubstanceSizeUtils
						.getMenuCheckMarkSize(SubstanceSizeUtils
								.getControlFontSize())),

				"CheckBoxMenuItem.disabledForeground",
				disabledForegroundColor,

				"CheckBoxMenuItem.foreground",
				foregroundColor,

				"CheckBoxMenuItem.margin",
				menuItemInsets,

				"CheckBoxMenuItem.selectionForeground",
				selectionCellForegroundColor,

				"ColorChooser.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JColorChooser.class, this, false),

				"ColorChooser.foreground",
				foregroundColor,

				"ComboBox.ancestorInputMap",
				new UIDefaults.LazyInputMap(new Object[] { "ESCAPE",
						"hidePopup",

						"PAGE_UP", "pageUpPassThrough",

						"PAGE_DOWN", "pageDownPassThrough",

						"HOME", "homePassThrough",

						"END", "endPassThrough",

						"DOWN", "selectNext",

						"KP_DOWN", "selectNext",

						"alt DOWN", "togglePopup",

						"alt KP_DOWN", "togglePopup",

						"alt UP", "togglePopup",

						"alt KP_UP", "togglePopup",

						"SPACE", "spacePopup",

						"ENTER", "enterPressed",

						"UP", "selectPrevious",

						"KP_UP", "selectPrevious" }),

				"ComboBox.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JComboBox.class, this, false),

				"ComboBox.border",
				comboBorder,

				"ComboBox.disabledBackground",
				backgroundLightColor,

				"ComboBox.disabledForeground",
				disabledForegroundColor,

				"ComboBox.foreground",
				foregroundColor,

				"ComboBox.selectionBackground",
				backgroundLightColor,

				"ComboBox.selectionForeground",
				foregroundColor,

				"DesktopIcon.border",
				regularMarginBorder,

				"DesktopIcon.width",
				new Integer(140),

				"Desktop.background",
				new ColorUIResource(new Color(0x0, true)),

				"Desktop.foreground",
				foregroundColor,

				"Dialog.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JDialog.class, this, false),

				"EditorPane.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JEditorPane.class, this, false),

				"EditorPane.border",
				textBorder,

				"EditorPane.foreground",
				foregroundColor,

				"EditorPane.caretForeground",
				foregroundColor,

				"EditorPane.disabledBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JEditorPane.class, this, true),

				"EditorPane.inactiveBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JEditorPane.class, this, true),

				"EditorPane.inactiveForeground",
				disabledTextComponentForegroundColor,

				"EditorPane.selectionBackground",
				selectionBackgroundColor,

				"EditorPane.selectionForeground",
				selectionForegroundColor,

				"FileChooser.upFolderIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/go-up.png");
					}
				},

				"FileChooser.newFolderIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/folder-new.png");
					}
				},

				"FileChooser.homeFolderIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/user-home.png");
					}
				},

				"FileChooser.listViewIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/application_view_list.png");
					}
				},

				"FileChooser.detailsViewIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/application_view_detail.png");
					}
				},

				"FileChooser.viewMenuLabelText",
				"View",

				"FileChooser.refreshActionLabelText",
				"Refresh",

				"FileChooser.newFolderActionLabelText",
				"New Folder",

				"FileChooser.listViewActionLabelText",
				"List",

				"FileChooser.detailsViewActionLabelText",
				"Details",

				"FileChooser.lookInLabelMnemonic",
				new Integer(KeyEvent.VK_I),

				"FileChooser.fileNameLabelMnemonic",
				new Integer(KeyEvent.VK_N),

				"FileChooser.filesOfTypeLabelMnemonic",
				new Integer(KeyEvent.VK_T),

				"FileChooser.usesSingleFilePane",
				Boolean.TRUE,

				"FileChooser.ancestorInputMap",
				new UIDefaults.LazyInputMap(new Object[] { "ESCAPE",
						"cancelSelection", "F2", "editFileName", "F5",
						"refresh", "BACK_SPACE", "Go Up", "ENTER",
						"approveSelection" }),

				"FileView.computerIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/computer.png");
					}
				},

				"FileView.directoryIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/folder.png");
					}
				},

				"FileView.fileIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/text-x-generic.png");
					}
				},

				"FileView.floppyDriveIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/media-floppy.png");
					}
				},

				"FileView.hardDriveIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/drive-harddisk.png");
					}
				},

				"FormattedTextField.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JFormattedTextField.class, this, false),

				"FormattedTextField.border",
				textBorder,

				"FormattedTextField.caretForeground",
				foregroundColor,

				"FormattedTextField.disabledBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JFormattedTextField.class, this, true),

				"FormattedTextField.foreground",
				foregroundColor,

				"FormattedTextField.inactiveBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JFormattedTextField.class, this, true),

				"FormattedTextField.inactiveForeground",
				disabledTextComponentForegroundColor,

				"FormattedTextField.selectionBackground",
				selectionBackgroundColor,

				"FormattedTextField.selectionForeground",
				selectionForegroundColor,

				"InternalFrame.activeTitleBackground",
				selectionForegroundColor,

				"InternalFrame.inactiveTitleBackground",
				foregroundColor,

				"InternalFrame.border",
				new BorderUIResource(new SubstancePaneBorder()),

				"InternalFrame.closeIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceImageCreator.getCloseIcon(
								titlePaneScheme, titlePaneScheme);
					}
				},

				"InternalFrame.iconifyIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceImageCreator.getMinimizeIcon(
								titlePaneScheme, titlePaneScheme);
					}
				},

				"InternalFrame.maximizeIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceImageCreator.getMaximizeIcon(
								titlePaneScheme, titlePaneScheme);
					}
				},

				"InternalFrame.minimizeIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceImageCreator.getRestoreIcon(
								titlePaneScheme, titlePaneScheme);
					}
				},

				"InternalFrame.paletteCloseIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceImageCreator
								.getCloseIcon(titlePaneScheme, titlePaneScheme);
					}
				},

				"Label.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(JLabel.class,
						this, false),

				"Label.foreground",
				foregroundColor,

				"Label.disabledText",
				disabledForegroundColor,

				"Label.disabledForeground",
				disabledForegroundColor,

				"List.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(JList.class,
						this, false),

				"List.cellRenderer",
				listCellRendererActiveValue,

				"List.focusCellHighlightBorder",
				new SubstanceBorder(new Insets(1, 1, 1, 1)),

				"List.focusSelectedCellHighlightBorder",
				new BorderUIResource.EmptyBorderUIResource(1, 1, 1, 1),

				"List.foreground",
				foregroundColor,

				"List.selectionBackground",
				selectionCellBackgroundColor,

				"List.selectionForeground",
				selectionCellForegroundColor,

				"Menu.arrowIcon",
				menuArrowIcon,

				"Menu.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(JMenu.class,
						this, false),

				"Menu.borderPainted",
				Boolean.FALSE,

				"Menu.checkIcon",
				null,

				"Menu.disabledForeground",
				disabledForegroundColor,

				"Menu.foreground",
				foregroundColor,

				"Menu.margin",
				menuItemInsets,

				"Menu.selectionForeground",
				selectionCellForegroundColor,

				"MenuBar.background",
				this.isRegisteredAsDecorationArea(DecorationAreaType.HEADER) ? new ColorUIResource(
						this
								.getMainActiveColorScheme(
										DecorationAreaType.HEADER)
								.getMidColor())
						: SubstanceColorUtilities.getDefaultBackgroundColor(
								JMenuBar.class, this, false),

				"MenuBar.foreground",
				new ColorUIResource(this.getMainActiveColorScheme(
						DecorationAreaType.HEADER).getForegroundColor()),

				"MenuBar.border",
				null,

				"MenuItem.acceleratorForeground",
				foregroundColor,

				"MenuItem.acceleratorSelectionForeground",
				foregroundColor,

				"MenuItem.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JMenuItem.class, this, false),

				"MenuItem.borderPainted",
				Boolean.FALSE,

				"MenuItem.checkIcon",
				null,

				"MenuItem.disabledForeground",
				disabledForegroundColor,

				"MenuItem.foreground",
				foregroundColor,

				"MenuItem.margin",
				menuItemInsets,

				"MenuItem.selectionForeground",
				selectionCellForegroundColor,

				"OptionPane.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JOptionPane.class, this, false),

				"OptionPane.errorIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/32/dialog-error.png");
					}
				},

				"OptionPane.foreground",
				foregroundColor,

				"OptionPane.informationIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/32/dialog-information.png");
					}
				},

				"OptionPane.messageForeground",
				foregroundColor,

				"OptionPane.questionIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/32/help-browser.png");
					}
				},

				"OptionPane.warningIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return SubstanceCoreUtilities
								.getIcon("resource/32/dialog-warning.png");
					}
				},

				"Panel.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(JPanel.class,
						this, false),

				"Panel.foreground",
				foregroundColor,

				"PasswordField.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JPasswordField.class, this, false),

				"PasswordField.border",
				textBorder,

				"PasswordField.caretForeground",
				foregroundColor,

				"PasswordField.disabledBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JPasswordField.class, this, true),

				"PasswordField.foreground",
				foregroundColor,

				"PasswordField.inactiveBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JPasswordField.class, this, true),

				"PasswordField.inactiveForeground",
				disabledTextComponentForegroundColor,

				"PasswordField.selectionBackground",
				selectionBackgroundColor,

				"PasswordField.selectionForeground",
				selectionForegroundColor,

				"PopupMenu.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JPopupMenu.class, this, false),

				"PopupMenu.border",
				regularMarginBorder,

				"ProgressBar.border",
				new BorderUIResource(new SubstanceBorder()),

				"ProgressBar.cycleTime",
				new Integer(1000),

				"ProgressBar.repaintInterval",
				new Integer(50),

				"ProgressBar.horizontalSize",
				new DimensionUIResource(146, SubstanceSizeUtils
						.getControlFontSize()),

				"ProgressBar.verticalSize",
				new DimensionUIResource(
						SubstanceSizeUtils.getControlFontSize(), 146),

				"ProgressBar.selectionBackground",
				foregroundColor,

				"ProgressBar.selectionForeground",
				foregroundColor,

				"RadioButton.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JRadioButton.class, this, false),

				"RadioButton.border",
				new BorderUIResource.CompoundBorderUIResource(
						SubstanceSizeUtils
								.getRadioButtonBorder(SubstanceSizeUtils
										.getControlFontSize()),
						new MarginBorder()),

				"RadioButton.foreground",
				foregroundColor,

				"RadioButton.disabledText",
				disabledForegroundColor,

				"RadioButtonMenuItem.acceleratorForeground",
				foregroundColor,

				"RadioButtonMenuItem.acceleratorSelectionForeground",
				foregroundColor,

				"RadioButtonMenuItem.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JRadioButtonMenuItem.class, this, false),

				"RadioButtonMenuItem.borderPainted",
				Boolean.FALSE,

				"RadioButtonMenuItem.checkIcon",
				new RadioButtonMenuItemIcon(null, SubstanceSizeUtils
						.getMenuCheckMarkSize(SubstanceSizeUtils
								.getControlFontSize())),

				"RadioButtonMenuItem.disabledForeground",
				disabledForegroundColor,

				"RadioButtonMenuItem.foreground",
				foregroundColor,

				"RadioButtonMenuItem.margin",
				menuItemInsets,

				"RadioButtonMenuItem.selectionForeground",
				selectionCellForegroundColor,

				"RootPane.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JRootPane.class, this, false),

				"RootPane.border",
				new SubstancePaneBorder(),

				"ScrollBar.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JScrollBar.class, this, false),

				"ScrollBar.width",
				new Integer(SubstanceSizeUtils
						.getScrollBarWidth(SubstanceSizeUtils
								.getControlFontSize())),

				"ScrollBar.minimumThumbSize",
				new DimensionUIResource(SubstanceSizeUtils
						.getScrollBarWidth(SubstanceSizeUtils
								.getControlFontSize()) - 2, SubstanceSizeUtils
						.getScrollBarWidth(SubstanceSizeUtils
								.getControlFontSize()) - 2),

				"ScrollPane.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JScrollPane.class, this, false),

				"ScrollPane.foreground",
				foregroundColor,

				"ScrollPane.border",
				new SubstanceScrollPaneBorder(),

				"Separator.background",
				backgroundDefaultColor,

				"Separator.foreground",
				lineBwColor,

				"Slider.altTrackColor",
				lineColor,

				"Slider.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JSlider.class, this, false),

				"Slider.darkShadow",
				lineColor,

				"Slider.focus",
				lineColor,

				"Slider.focusInsets",
				new InsetsUIResource(2, 2, 0, 2),

				"Slider.foreground",
				lineColor,

				"Slider.highlight",
				backgroundLightColor,

				"Slider.shadow",
				lineColor,

				"Slider.tickColor",
				foregroundColor,

				"Spinner.arrowButtonInsets",
				SubstanceSizeUtils
						.getSpinnerArrowButtonInsets(SubstanceSizeUtils
								.getControlFontSize()),

				"Spinner.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JSpinner.class, this, false),

				"Spinner.border",
				spinnerBorder,

				"Spinner.disableOnBoundaryValues",
				Boolean.TRUE,

				"Spinner.foreground",
				foregroundColor,

				"Spinner.editorBorderPainted",
				Boolean.TRUE,

				"SplitPane.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JSplitPane.class, this, false),

				"SplitPane.foreground",
				foregroundColor,

				"SplitPane.dividerFocusColor",
				backgroundDefaultColor,

				"SplitPaneDivider.draggingColor",
				backgroundActiveColor,

				"SplitPane.border",
				new BorderUIResource(new EmptyBorder(0, 0, 0, 0)),

				"SplitPane.dividerSize",
				(int) (SubstanceSizeUtils.getArrowIconWidth(SubstanceSizeUtils
						.getControlFontSize()) + SubstanceSizeUtils
						.getAdjustedSize(SubstanceSizeUtils
								.getControlFontSize(), -1, 6, -1, true)),

				"SplitPaneDivider.border",
				new BorderUIResource(new EmptyBorder(1, 1, 1, 1)),

				"TabbedPane.tabAreaBackground",
				backgroundDefaultColor,

				"TabbedPane.unselectedBackground",
				backgroundDefaultColor,

				"TabbedPane.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTabbedPane.class, this, false),

				"TabbedPane.borderHightlightColor",
				new ColorUIResource(mainActiveScheme.getMidColor()),

				"TabbedPane.contentAreaColor",
				null,

				"TabbedPane.contentBorderInsets",
				new InsetsUIResource(4, 4, 4, 4),

				"TabbedPane.contentOpaque",
				Boolean.FALSE,

				"TabbedPane.darkShadow",
				new ColorUIResource(getColorScheme(null,
						ColorSchemeAssociationKind.BORDER,
						ComponentState.SELECTED).getLineColor()),

				"TabbedPane.focus",
				foregroundColor,

				"TabbedPane.foreground",
				foregroundColor,

				"TabbedPane.highlight",
				new ColorUIResource(mainActiveScheme.getLightColor()),

				"TabbedPane.light",
				mainDefaultScheme.isDark() ? new ColorUIResource(
						SubstanceColorUtilities.getAlphaColor(mainDefaultScheme
								.getUltraDarkColor(), 100))
						: new ColorUIResource(mainDefaultScheme.getLightColor()),

				"TabbedPane.selected",
				new ColorUIResource(mainActiveScheme.getExtraLightColor()),

				"TabbedPane.selectedForeground",
				foregroundColor,

				"TabbedPane.selectHighlight",
				new ColorUIResource(mainActiveScheme.getMidColor()),

				"TabbedPane.shadow",
				new ColorUIResource(SubstanceColorUtilities
						.getInterpolatedColor(mainDefaultScheme
								.getExtraLightColor(), mainDefaultScheme
								.getLightColor(), 0.5)),

				"TabbedPane.tabRunOverlay",
				new Integer(0),

				"Table.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(JTable.class,
						this, false),

				"Table.focusCellBackground",
				backgroundActiveColor,

				"Table.focusCellForeground",
				foregroundColor,

				"Table.focusCellHighlightBorder",
				new SubstanceBorder(),

				"Table.foreground",
				foregroundColor,

				"Table.gridColor",
				lineColorDefault,

				"Table.scrollPaneBorder",
				new SubstanceScrollPaneBorder(),

				"Table.selectionBackground",
				selectionCellBackgroundColor,

				"Table.selectionForeground",
				selectionCellForegroundColor,

				"TableHeader.cellBorder",
				null,

				"TableHeader.foreground",
				foregroundColor,

				"TableHeader.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTableHeader.class, this, false),

				"TextArea.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextArea.class, this, false),

				"TextArea.border",
				textBorder,

				"TextArea.caretForeground",
				foregroundColor,

				"TextArea.disabledBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextArea.class, this, true),

				"TextArea.foreground",
				foregroundColor,

				"TextArea.inactiveBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextArea.class, this, true),

				"TextArea.inactiveForeground",
				disabledTextComponentForegroundColor,

				"TextArea.selectionBackground",
				selectionBackgroundColor,

				"TextArea.selectionForeground",
				selectionForegroundColor,

				"TextField.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextField.class, this, false),

				"TextField.border",
				textBorder,

				"TextField.caretForeground",
				foregroundColor,

				"TextField.disabledBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextField.class, this, true),

				"TextField.foreground",
				foregroundColor,

				"TextField.inactiveBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextField.class, this, true),

				"TextField.inactiveForeground",
				disabledTextComponentForegroundColor,

				"TextField.selectionBackground",
				selectionBackgroundColor,

				"TextField.selectionForeground",
				selectionForegroundColor,

				"TextPane.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextPane.class, this, false),

				"TextPane.border",
				textBorder,

				"TextPane.disabledBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextPane.class, this, true),

				"TextPane.foreground",
				foregroundColor,

				"TextPane.caretForeground",
				foregroundColor,

				"TextPane.inactiveBackground",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextPane.class, this, true),

				"TextPane.inactiveForeground",
				disabledTextComponentForegroundColor,

				"TextPane.selectionBackground",
				selectionBackgroundColor,

				"TextPane.selectionForeground",
				selectionForegroundColor,

				"TitledBorder.titleColor",
				foregroundColor,

				"TitledBorder.border",
				new SubstanceEtchedBorder(),

				"ToggleButton.foreground",
				foregroundColor,

				"ToggleButton.disabledText",
				disabledForegroundColor,

				"ToggleButton.margin",
				new InsetsUIResource(0, 0, 0, 0),

				"ToolBar.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JToolBar.class, this, false),

				"ToolBar.border",
				new BorderUIResource(new SubstanceToolBarBorder()),

				"ToolBar.isRollover",
				Boolean.TRUE,

				"ToolBar.foreground",
				foregroundColor,

				"ToolBarSeparator.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JToolBar.class, this, false),

				"ToolBarSeparator.foreground",
				lineBwColor,

				"ToolBar.separatorSize",
				null,

				"ToolTip.border",
				tooltipBorder,

				"ToolTip.borderInactive",
				tooltipBorder,

				"ToolTip.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JToolTip.class, this, false),

				"ToolTip.backgroundInactive",
				SubstanceColorUtilities.getDefaultBackgroundColor(
						JTextPane.class, this, true),

				"ToolTip.foreground",
				foregroundColor,

				"ToolTip.foregroundInactive",
				disabledForegroundColor,

				"Tree.closedIcon",
				emptyIcon,

				"Tree.collapsedIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return new IconUIResource(SubstanceIconFactory
								.getTreeIcon(null, true));
					}
				},

				"Tree.expandedIcon",
				new UIDefaults.LazyValue() {
					public Object createValue(UIDefaults table) {
						return new IconUIResource(SubstanceIconFactory
								.getTreeIcon(null, false));
					}
				},

				"Tree.leafIcon",
				emptyIcon,

				"Tree.openIcon",
				emptyIcon,

				"Tree.background",
				SubstanceColorUtilities.getDefaultBackgroundColor(JTree.class,
						this, false),

				"Tree.selectionBackground", selectionCellBackgroundColor,

				"Tree.foreground", foregroundColor,

				"Tree.hash", lineColorDefault,

				"Tree.rowHeight", new Integer(0),

				"Tree.selectionBorderColor", lineColor,

				"Tree.selectionForeground", selectionCellForegroundColor,

				"Tree.textBackground", backgroundDefaultColor,

				"Tree.textForeground", foregroundColor,

				"Viewport.background", backgroundDefaultColor,

				"Viewport.foreground", foregroundColor,

		};
		table.putDefaults(defaults);
	}

	/**
	 * Returns the color scheme of the specified component in the specified
	 * component state.
	 * 
	 * @param comp
	 *            Component.
	 * @param componentState
	 *            Component state.
	 * @return The color scheme of the component in the specified component
	 *         state.
	 */
	public SubstanceColorScheme getColorScheme(Component comp,
			ComponentState componentState) {
		return this.getColorScheme(comp, componentState, false);
	}

	/**
	 * Returns the color scheme of the specified component in the specified
	 * component state.
	 * 
	 * @param comp
	 *            Component.
	 * @param componentState
	 *            Component state.
	 * @param toIgnoreHighlights
	 *            If <code>true</code>, the highlight scheme map will not be
	 *            consulted.
	 * @return The color scheme of the component in the specified component
	 *         state.
	 */
	public SubstanceColorScheme getColorScheme(Component comp,
			ComponentState componentState, boolean toIgnoreHighlights) {
		// small optimization - lookup the decoration area only if there
		// are decoration-specific scheme bundles.
		if (this.colorSchemeBundleMap.size() > 1) {
			DecorationAreaType decorationAreaType = SubstanceDecorationUtilities
					.getDecorationType(comp);
			if ((decorationAreaType == DecorationAreaType.NONE)
					&& (componentState == ComponentState.DEFAULT)) {
				return this.defaultColorScheme;
			}
			if (this.colorSchemeBundleMap.containsKey(decorationAreaType)) {
				SubstanceColorScheme registered = this.colorSchemeBundleMap
						.get(decorationAreaType).getColorScheme(comp,
								componentState, toIgnoreHighlights);
				if (registered != null)
					return registered;
			}
		}

		if (componentState == ComponentState.DEFAULT)
			return this.defaultColorScheme;

		SubstanceColorScheme registered = this.colorSchemeBundleMap.get(
				DecorationAreaType.NONE).getColorScheme(comp, componentState,
				toIgnoreHighlights);
		if (registered != null)
			return registered;

		if (componentState == ComponentState.DEFAULT)
			return this.getMainDefaultColorScheme();
		if (componentState.getColorSchemeKind() == ColorSchemeKind.DISABLED)
			return this.getMainDisabledColorScheme();
		return this.getMainActiveColorScheme();
	}

	/**
	 * Returns the highlight color scheme of the component.
	 * 
	 * @param comp
	 *            Component.
	 * @param componentState
	 *            Component state.
	 * @return Component highlight color scheme.
	 * @deprecated Use
	 *             {@link #getColorScheme(Component, ColorSchemeAssociationKind, ComponentState)}
	 *             with {@link ColorSchemeAssociationKind#HIGHLIGHT} instead.
	 *             This method will be removed in version 6.0.
	 */
	public SubstanceColorScheme getHighlightColorScheme(Component comp,
			ComponentState componentState) {
		// small optimization - lookup the decoration area only if there
		// are decoration-specific scheme bundles.
		if (this.colorSchemeBundleMap.size() > 1) {
			DecorationAreaType decorationAreaType = SubstanceDecorationUtilities
					.getDecorationType(comp);
			if (this.colorSchemeBundleMap.containsKey(decorationAreaType)) {
				SubstanceColorScheme registered = this.colorSchemeBundleMap
						.get(decorationAreaType).getColorScheme(comp,
								ColorSchemeAssociationKind.HIGHLIGHT,
								componentState);
				if (registered != null)
					return registered;
			}
		}

		SubstanceColorScheme registered = this.colorSchemeBundleMap.get(
				DecorationAreaType.NONE).getColorScheme(comp,
				ColorSchemeAssociationKind.HIGHLIGHT, componentState);
		if (registered != null)
			return registered;

		if (componentState == ComponentState.DEFAULT)
			return this.getMainDefaultColorScheme();
		if (componentState.getColorSchemeKind() == ColorSchemeKind.DISABLED)
			return this.getMainDisabledColorScheme();
		return this.getMainActiveColorScheme();
	}

	/**
	 * Returns the alpha channel of the highlight color scheme of the component.
	 * 
	 * @param comp
	 *            Component.
	 * @param componentState
	 *            Component state.
	 * @return Highlight color scheme alpha channel.
	 */
	public float getHighlightAlpha(Component comp, ComponentState componentState) {
		// small optimization - lookup the decoration area only if there
		// are decoration-specific scheme bundles.
		if (this.colorSchemeBundleMap.size() > 1) {
			DecorationAreaType decorationAreaType = SubstanceDecorationUtilities
					.getDecorationType(comp);
			if (this.colorSchemeBundleMap.containsKey(decorationAreaType)) {
				Float registered = this.colorSchemeBundleMap.get(
						decorationAreaType).getHighlightAlpha(comp,
						componentState);
				if (registered >= 0.0)
					return registered;
			}
		}

		Float registered = this.colorSchemeBundleMap.get(
				DecorationAreaType.NONE)
				.getHighlightAlpha(comp, componentState);
		if (registered >= 0.0)
			return registered;

		boolean isRollover = componentState.isKindActive(FadeKind.ROLLOVER);
		boolean isSelected = componentState.isKindActive(FadeKind.SELECTION);
		boolean isArmed = componentState.isKindActive(FadeKind.ARM);

		if (isRollover && isSelected)
			return 0.9f;
		if (isRollover && isArmed)
			return 0.8f;
		if (isSelected)
			return 0.7f;
		if (isArmed)
			return 0.6f;
		if (isRollover)
			return 0.4f;
		return 0.0f;
	}

	/**
	 * Returns the alpha channel of the color scheme of the component.
	 * 
	 * @param comp
	 *            Component.
	 * @param componentState
	 *            Component state.
	 * @return Color scheme alpha channel.
	 */
	public float getAlpha(Component comp, ComponentState componentState) {
		// optimization - if the state is not registered in any
		// scheme bundle with custom alpha, return 1.0
		if (!this.statesWithAlpha.contains(componentState))
			return 1.0f;

		// small optimization - lookup the decoration area only if there
		// are decoration-specific scheme bundles.
		if (this.colorSchemeBundleMap.size() > 1) {
			DecorationAreaType decorationAreaType = SubstanceDecorationUtilities
					.getDecorationType(comp);
			if (this.colorSchemeBundleMap.containsKey(decorationAreaType)) {
				Float registered = this.colorSchemeBundleMap.get(
						decorationAreaType).getAlpha(comp, componentState);
				if (registered >= 0.0)
					return registered;
			}
		}

		Float registered = this.colorSchemeBundleMap.get(
				DecorationAreaType.NONE).getAlpha(comp, componentState);
		if (registered >= 0.0)
			return registered;

		return 1.0f;
	}

	/**
	 * Registers the specified color scheme bundle and background color scheme
	 * to be used on controls in decoration areas.
	 * 
	 * @param bundle
	 *            The color scheme bundle to use on controls in decoration
	 *            areas.
	 * @param backgroundColorScheme
	 *            The color scheme to use for background of controls in
	 *            decoration areas.
	 * @param areaTypes
	 *            Enumerates the area types that are affected by the parameters.
	 */
	public void registerDecorationAreaSchemeBundle(
			SubstanceColorSchemeBundle bundle,
			SubstanceColorScheme backgroundColorScheme,
			DecorationAreaType... areaTypes) {
		if (bundle == null)
			return;

		for (DecorationAreaType areaType : areaTypes) {
			this.decoratedAreaSet.add(areaType);
			this.colorSchemeBundleMap.put(areaType, bundle);
			this.backgroundColorSchemeMap.put(areaType, backgroundColorScheme);

			if (areaType == DecorationAreaType.NONE) {
				this.defaultColorScheme = bundle.getDefaultColorScheme();
			}
		}
		this.statesWithAlpha.addAll(bundle.getStatesWithAlpha());
	}

	/**
	 * Registers the specified color scheme bundle to be used on controls in
	 * decoration areas.
	 * 
	 * @param bundle
	 *            The color scheme bundle to use on controls in decoration
	 *            areas.
	 * @param areaTypes
	 *            Enumerates the area types that are affected by the parameters.
	 */
	public void registerDecorationAreaSchemeBundle(
			SubstanceColorSchemeBundle bundle, DecorationAreaType... areaTypes) {
		this.registerDecorationAreaSchemeBundle(bundle, bundle
				.getDefaultColorScheme(), areaTypes);
	}

	/**
	 * Registers the specified background color scheme to be used on controls in
	 * decoration areas.
	 * 
	 * @param backgroundColorScheme
	 *            The color scheme to use for background of controls in
	 *            decoration areas.
	 * @param areaTypes
	 *            Enumerates the area types that are affected by the parameters.
	 *            Each decoration area type will be painted by
	 *            {@link SubstanceDecorationPainter#paintDecorationArea(Graphics2D, Component, DecorationAreaType, int, int, SubstanceSkin)}
	 *            .
	 */
	public void registerAsDecorationArea(
			SubstanceColorScheme backgroundColorScheme,
			DecorationAreaType... areaTypes) {
		for (DecorationAreaType areaType : areaTypes) {
			this.decoratedAreaSet.add(areaType);
			this.backgroundColorSchemeMap.put(areaType, backgroundColorScheme);
		}
	}

	/**
	 * Returns indication whether the specified decoration area type should have
	 * their background painted by
	 * {@link SubstanceDecorationPainter#paintDecorationArea(Graphics2D, Component, DecorationAreaType, int, int, SubstanceSkin)}
	 * instead of a simple background fill.
	 * 
	 * @param decorationType
	 *            Decoration area type.
	 * @return <code>true</code> if specified decoration area type should have
	 *         their background painted by
	 *         {@link SubstanceDecorationPainter#paintDecorationArea(Graphics2D, Component, DecorationAreaType, int, int, SubstanceSkin)}
	 *         , <code>false</code> otherwise.
	 */
	public boolean isRegisteredAsDecorationArea(
			DecorationAreaType decorationType) {
		return this.decoratedAreaSet.contains(decorationType);
	}

	/**
	 * Returns the color scheme to be used for painting the watermark. If no
	 * custom watermark color scheme is specified ({@link #watermarkScheme} is
	 * <code>null</code>), the main default color scheme of this skin is used.
	 * 
	 * @return The color scheme to be used for painting the watermark.
	 */
	public SubstanceColorScheme getWatermarkColorScheme() {
		if (this.watermarkScheme != null) {
			return this.watermarkScheme;
		}

		return this.getMainDefaultColorScheme();
	}

	/**
	 * Returns the main active color scheme for the specific decoration area
	 * type. Custom painting code that needs to consult the colors of the
	 * specific component should use
	 * {@link #getColorScheme(Component, ComponentState)} method and various
	 * {@link SubstanceColorScheme} methods.
	 * 
	 * @param decorationAreaType
	 *            Decoration area type.
	 * @return The main active color scheme for this skin.
	 * @see #getColorScheme(Component, ComponentState)
	 * @see #getMainActiveColorScheme()
	 */
	public SubstanceColorScheme getMainActiveColorScheme(
			DecorationAreaType decorationAreaType) {
		if (this.colorSchemeBundleMap.containsKey(decorationAreaType))
			return this.colorSchemeBundleMap.get(decorationAreaType)
					.getActiveColorScheme();
		return this.colorSchemeBundleMap.get(DecorationAreaType.NONE)
				.getActiveColorScheme();
	}

	/**
	 * Returns the main default color scheme for the specific decoration area
	 * type. Custom painting code that needs to consult the colors of the
	 * specific component should use
	 * {@link #getColorScheme(Component, ComponentState)} method and various
	 * {@link SubstanceColorScheme} methods.
	 * 
	 * @param decorationAreaType
	 *            Decoration area type.
	 * @return The main default color scheme for this skin.
	 * @see #getColorScheme(Component, ComponentState)
	 * @see #getMainDefaultColorScheme()
	 */
	public SubstanceColorScheme getMainDefaultColorScheme(
			DecorationAreaType decorationAreaType) {
		if (this.colorSchemeBundleMap.containsKey(decorationAreaType))
			return this.colorSchemeBundleMap.get(decorationAreaType)
					.getDefaultColorScheme();
		return this.colorSchemeBundleMap.get(DecorationAreaType.NONE)
				.getDefaultColorScheme();
	}

	/**
	 * Returns the main disabled color scheme for the specific decoration area
	 * type. Custom painting code that needs to consult the colors of the
	 * specific component should use
	 * {@link #getColorScheme(Component, ComponentState)} method and various
	 * {@link SubstanceColorScheme} methods.
	 * 
	 * @param decorationAreaType
	 *            Decoration area type.
	 * @return The main disabled color scheme for this skin.
	 * @see #getColorScheme(Component, ComponentState)
	 * @see #getMainDisabledColorScheme()
	 */
	public SubstanceColorScheme getMainDisabledColorScheme(
			DecorationAreaType decorationAreaType) {
		if (this.colorSchemeBundleMap.containsKey(decorationAreaType))
			return this.colorSchemeBundleMap.get(decorationAreaType)
					.getDisabledColorScheme();
		return this.colorSchemeBundleMap.get(DecorationAreaType.NONE)
				.getDisabledColorScheme();
	}

	/**
	 * Returns the start of fade effect on the selected tabs in
	 * {@link JTabbedPane}s. This value can be used to create XP-like "headers"
	 * on the selected tabs.
	 * 
	 * @return The start of fade effect on the selected tabs in
	 *         {@link JTabbedPane}s.
	 * @see #getSelectedTabFadeEnd()
	 */
	public double getSelectedTabFadeStart() {
		return this.selectedTabFadeStart;
	}

	/**
	 * Returns the end of fade effect on the selected tabs in
	 * {@link JTabbedPane
	 * }s. This value can be used to create XP-like "headers"
	 * on the selected tabs.
	 * 
	 * @return The end of fade effect on the selected tabs in
	 *         {@link JTabbedPane
	 * 	}s.
	 * @see #getSelectedTabFadeStart()
	 */
	public double getSelectedTabFadeEnd() {
		return this.selectedTabFadeEnd;
	}

	/**
	 * Sets the end of fade effect on the selected tabs in {@link JTabbedPane}s.
	 * The value should be in 0.0-1.0 range.
	 * 
	 * @param selectedTabFadeEnd
	 *            The end of fade effect on the selected tabs in
	 *            {@link JTabbedPane}s. Should be in 0.0-1.0 range.
	 */
	public void setSelectedTabFadeEnd(double selectedTabFadeEnd) {
		if ((selectedTabFadeEnd < 0.0) || (selectedTabFadeEnd > 1.0)) {
			throw new IllegalArgumentException(
					"Value for selected tab fade end should be in 0.0-1.0 range");
		}
		this.selectedTabFadeEnd = selectedTabFadeEnd;
	}

	/**
	 * Sets the start of fade effect on the selected tabs in {@link JTabbedPane}
	 * s. The value should be in 0.0-1.0 range.
	 * 
	 * @param selectedTabFadeStart
	 *            The start of fade effect on the selected tabs in
	 *            {@link JTabbedPane} s. Should be in 0.0-1.0 range.
	 */
	public void setSelectedTabFadeStart(double selectedTabFadeStart) {
		if ((selectedTabFadeStart < 0.0) || (selectedTabFadeStart > 1.0)) {
			throw new IllegalArgumentException(
					"Value for selected tab fade start should be in 0.0-1.0 range");
		}
		this.selectedTabFadeStart = selectedTabFadeStart;
	}

	/**
	 * Returns indication whether the specified decoration area type should
	 * paint drop shadows on the top few pixels.
	 * 
	 * @param decorationAreaType
	 *            Decoration area type.
	 * @return <code>true</code> if the specified decoration area type should
	 *         paint drop shadows on the top few pixels, <code>false</code>
	 *         otherwise.
	 * @see #setPaintingDropShadows(DecorationAreaType)
	 * @see #unsetPaintingDropShadows(DecorationAreaType)
	 */
	public boolean isPaintingDropShadows(DecorationAreaType decorationAreaType) {
		return this.dropShadowsSet.contains(decorationAreaType);
	}

	/**
	 * Marks the specified area type to have drop shadows on the top few pixels.
	 * 
	 * @param decorationAreaType
	 *            Decoration areas of this type will have drop shadows on the
	 *            top few pixels.
	 * @see #unsetPaintingDropShadows(DecorationAreaType)
	 * @see #isPaintingDropShadows(DecorationAreaType)
	 */
	public void setPaintingDropShadows(DecorationAreaType decorationAreaType) {
		this.dropShadowsSet.add(decorationAreaType);
	}

	/**
	 * Marks the specified area type to not have drop shadows on the top few
	 * pixels.
	 * 
	 * @param decorationAreaType
	 *            Decoration areas of this type will not have drop shadows on
	 *            the top few pixels.
	 * @see #setPaintingDropShadows(DecorationAreaType)
	 * @see #isPaintingDropShadows(DecorationAreaType)
	 */
	public void unsetPaintingDropShadows(DecorationAreaType decorationAreaType) {
		this.dropShadowsSet.remove(decorationAreaType);
	}

	/**
	 * Returns the color scheme to be used for painting the specified visual
	 * area of the component under the specified component state.
	 * 
	 * @param comp
	 *            Component.
	 * @param associationKind
	 *            Color scheme association kind.
	 * @param componentState
	 *            Component state.
	 * @return Color scheme to be used for painting the specified visual area of
	 *         the component under the specified component state.
	 * @since version 5.1
	 */
	public SubstanceColorScheme getColorScheme(Component comp,
			ColorSchemeAssociationKind associationKind,
			ComponentState componentState) {
		// small optimization - lookup the decoration area only if there
		// are decoration-specific scheme bundles.
		if (this.colorSchemeBundleMap.size() > 1) {
			DecorationAreaType decorationAreaType = SubstanceDecorationUtilities
					.getDecorationType(comp);
			if (this.colorSchemeBundleMap.containsKey(decorationAreaType)) {
				return this.colorSchemeBundleMap.get(decorationAreaType)
						.getColorScheme(comp, associationKind, componentState);
			}
		}
		return this.colorSchemeBundleMap.get(DecorationAreaType.NONE)
				.getColorScheme(comp, associationKind, componentState);
	}

	/**
	 * Returns the border color scheme of the component.
	 * 
	 * @param comp
	 *            Component.
	 * @param componentState
	 *            Component state.
	 * @return Component border color scheme.
	 * @deprecated Use
	 *             {@link #getColorScheme(Component, ColorSchemeAssociationKind, ComponentState)}
	 *             with {@link ColorSchemeAssociationKind#BORDER} instead. This
	 *             method will be removed in version 6.0.
	 */
	@Deprecated
	public SubstanceColorScheme getBorderColorScheme(Component comp,
			ComponentState componentState) {
		// small optimization - lookup the decoration area only if there
		// are decoration-specific scheme bundles.
		if (this.colorSchemeBundleMap.size() > 1) {
			DecorationAreaType decorationAreaType = SubstanceDecorationUtilities
					.getDecorationType(comp);
			if (this.colorSchemeBundleMap.containsKey(decorationAreaType)) {
				return this.colorSchemeBundleMap.get(decorationAreaType)
						.getColorScheme(comp,
								ColorSchemeAssociationKind.BORDER,
								componentState);
			}
		}
		return this.colorSchemeBundleMap.get(DecorationAreaType.NONE)
				.getColorScheme(comp, ColorSchemeAssociationKind.BORDER,
						componentState);
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		final String myName = this.getDisplayName();
		SubstanceSkin clone = new SubstanceSkin() {
			public String getDisplayName() {
				return "Clone " + myName;
			}
		};
		clone.borderPainter = this.borderPainter;
		clone.buttonShaper = this.buttonShaper;
		clone.decorationPainter = this.decorationPainter;
		clone.gradientPainter = this.gradientPainter;
		clone.highlightPainter = this.highlightPainter;
		clone.watermark = this.watermark;
		clone.watermarkScheme = this.watermarkScheme;
		clone.selectedTabFadeEnd = this.selectedTabFadeEnd;
		clone.selectedTabFadeStart = this.selectedTabFadeStart;

		if (this.colorSchemeBundleMap != null) {
			clone.colorSchemeBundleMap = new EnumMap<DecorationAreaType, SubstanceColorSchemeBundle>(
					DecorationAreaType.class);
			for (Map.Entry<DecorationAreaType, SubstanceColorSchemeBundle> bundleEntry : this.colorSchemeBundleMap
					.entrySet()) {
				clone.colorSchemeBundleMap.put(bundleEntry.getKey(),
						(SubstanceColorSchemeBundle) bundleEntry.getValue()
								.clone());
			}
		}

		if (this.decoratedAreaSet != null) {
			clone.decoratedAreaSet = EnumSet.copyOf(this.decoratedAreaSet);
		}

		if (this.dropShadowsSet != null) {
			clone.dropShadowsSet = EnumSet.copyOf(this.dropShadowsSet);
		}

		if (this.backgroundColorSchemeMap != null) {
			clone.backgroundColorSchemeMap = new EnumMap<DecorationAreaType, SubstanceColorScheme>(
					this.backgroundColorSchemeMap);
		}
		return clone;
	}

	/**
	 * Creates a new skin that has the same settings as this skin with the
	 * addition of applying the specified color scheme transformation on all the
	 * relevant color schemes.
	 * 
	 * @param transform
	 *            Color scheme transformation.
	 * @param name
	 *            The name of the new skin.
	 * @return The new skin.
	 */
	@SubstanceApi
	public SubstanceSkin transform(ColorSchemeTransform transform,
			final String name) {
		SubstanceSkin result = new SubstanceSkin() {
			public String getDisplayName() {
				return name;
			}
		};
		// same painters
		result.borderPainter = this.borderPainter;
		result.buttonShaper = this.buttonShaper;
		result.decorationPainter = this.decorationPainter;
		result.gradientPainter = this.gradientPainter;
		result.highlightPainter = this.highlightPainter;
		// same watermark and transformed scheme
		result.watermark = this.watermark;
		if (this.watermarkScheme != null)
			result.watermarkScheme = transform.transform(this.watermarkScheme);
		// same misc settings
		result.selectedTabFadeEnd = this.selectedTabFadeEnd;
		result.selectedTabFadeStart = this.selectedTabFadeStart;

		// transform the scheme bundles
		if (this.colorSchemeBundleMap != null) {
			result.colorSchemeBundleMap = new EnumMap<DecorationAreaType, SubstanceColorSchemeBundle>(
					DecorationAreaType.class);
			for (Map.Entry<DecorationAreaType, SubstanceColorSchemeBundle> bundleEntry : this.colorSchemeBundleMap
					.entrySet()) {
				result.colorSchemeBundleMap.put(bundleEntry.getKey(),
						bundleEntry.getValue().transform(transform));
			}
		}

		// same set of decoration areas
		if (this.decoratedAreaSet != null) {
			result.decoratedAreaSet = EnumSet.copyOf(this.decoratedAreaSet);
		}
		// same set of drop shadow decorated areas
		if (this.dropShadowsSet != null) {
			result.dropShadowsSet = EnumSet.copyOf(this.dropShadowsSet);
		}
		// transform the background schemes
		// transform the state-specific highlight schemes
		if (this.backgroundColorSchemeMap != null) {
			result.backgroundColorSchemeMap = new EnumMap<DecorationAreaType, SubstanceColorScheme>(
					DecorationAreaType.class);
			for (Map.Entry<DecorationAreaType, SubstanceColorScheme> entry : this.backgroundColorSchemeMap
					.entrySet()) {
				result.backgroundColorSchemeMap.put(entry.getKey(), transform
						.transform(entry.getValue()));
			}
		}
		return result;
	}

	/**
	 * Creates a clone of this skin with the specified watermark. This is mainly
	 * targeting sample applications and docrobot. Applications are encouraged
	 * to create a custom skin by either extending one of the core Substance
	 * skins, or by extending the {@link SubstanceSkin} directly.
	 * 
	 * @param watermark
	 *            Watermark for the new skin.
	 * @return A clone of this skin with the specified watermark.
	 */
	@SubstanceApi
	public SubstanceSkin withWatermark(SubstanceWatermark watermark) {
		try {
			SubstanceSkin clone = (SubstanceSkin) this.clone();
			clone.watermark = watermark;
			return clone;
		} catch (CloneNotSupportedException cnse) {
			return null;
		}
	}

	/**
	 * Returns the background color scheme for the specified decoration area
	 * type. This method is mainly for the internal use of
	 * {@link SubstanceDecorationPainter#paintDecorationArea(Graphics2D, Component, DecorationAreaType, int, int, SubstanceSkin)}
	 * , but can be used in applications that wish to provide custom overlay
	 * background painting (such as watermarks, for example).
	 * 
	 * @param decorationAreaType
	 *            Decoration area type.
	 * @return The background color scheme for the specified decoration area
	 *         type.
	 */
	public SubstanceColorScheme getBackgroundColorScheme(
			DecorationAreaType decorationAreaType) {
		// 1 - check the registered background scheme for this specific area
		// type.
		if (this.backgroundColorSchemeMap.containsKey(decorationAreaType))
			return this.backgroundColorSchemeMap.get(decorationAreaType);
		// 2 - check the registered scheme bundle for this specific area type.
		if (this.colorSchemeBundleMap.containsKey(decorationAreaType)) {
			SubstanceColorScheme registered = this.colorSchemeBundleMap.get(
					decorationAreaType).getDefaultColorScheme();
			if (registered != null)
				return registered;
		}
		// 3 - return the background scheme for the default area type
		return this.backgroundColorSchemeMap.get(DecorationAreaType.NONE);
	}

	/**
	 * Checks whether this skin is valid. A valid skin must have a color scheme
	 * bundle for {@link DecorationAreaType#NONE} and non-<code>null</code>
	 * button shaper, gradient painter, border painter, highlight painter and
	 * decoration painter. If call to
	 * {@link SubstanceLookAndFeel#setSkin(String)} or
	 * {@link SubstanceLookAndFeel#setSkin(SubstanceSkin)} does not seem to have
	 * any visible effect (returning <code>false</code>), call this method to
	 * verify that your skin is valid.
	 * 
	 * @return <code>true</code> if this skin is valid, <code>false</code>
	 *         otherwise.
	 */
	public boolean isValid() {
		if (!this.colorSchemeBundleMap.containsKey(DecorationAreaType.NONE))
			return false;
		if (this.getButtonShaper() == null) {
			return false;
		}
		if (this.getGradientPainter() == null) {
			return false;
		}
		if (this.getBorderPainter() == null) {
			return false;
		}
		if (this.getHighlightPainter() == null) {
			return false;
		}
		if (this.getDecorationPainter() == null) {
			return false;
		}
		return true;
	}
}
